/*
 * Copyright © 2004-2014 chenYuan. All rights reserved.
 * @Website:wwww.jspx.net
 * @Mail:39793751@qq.com
 * @author: chenYuan , 陈原
 * @License: Jspx.net Framework Code is open source (LGPL)，Jspx.net Framework 使用LGPL 开源授权协议发布。
 * @jvm:jdk1.6+  x86/amd64
 *
 */
package com.jspx.txweb.proxy;

import com.jspx.boot.EnvFactory;
import com.jspx.boot.sign.ErrorRocCode;
import org.slf4j.Logger;
import com.jspx.boot.environment.Environment;
import com.jspx.boot.environment.Placeholder;
import org.slf4j.LoggerFactory;
import com.jspx.json.JSONObject;
import com.jspx.sioc.BeanFactory;
import com.jspx.txweb.*;
import com.jspx.txweb.annotation.Redirect;
import com.jspx.txweb.config.ActionConfig;
import com.jspx.txweb.config.ResultConfigBean;
import com.jspx.txweb.config.TXWebConfigManager;
import com.jspx.txweb.env.TXWeb;
import com.jspx.txweb.result.*;
import com.jspx.txweb.support.ActionSupport;
import com.jspx.txweb.util.TXWebUtil;
import com.jspx.utils.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;

/**
 * Created by IntelliJ IDEA.
 * User:chenYuan (mail:39793751@qq.com)
 * Date: 2006-12-27
 * Time: 0:20:12
 * action 参数 执行容器
 */
public class DefaultActionInvocation implements ActionInvocation {
    final private static Logger log = LoggerFactory.getLogger(DefaultActionInvocation.class);
    final private static String[] noParameter = new String[]{"session", "request", "response", "language", "config", "env", "actionLogTitle", "actionLogContent", "result", "templatePath", "actionResult"};
    final private static WebConfigManager webConfigManager = TXWebConfigManager.getInstance();
    final private static BeanFactory beanFactory = EnvFactory.getBeanFactory();
    final private static Map<String, String> resultMap = new HashMap<String, String>(22);

    static {
        resultMap.put(ActionSupport.NONE, NoneResult.class.getName());
        resultMap.put(ActionSupport.TEMPLATE, TemplateResult.class.getName());
        resultMap.put(ActionSupport.HtmlImg, HtmlImgResult.class.getName());
        resultMap.put(ActionSupport.HtmlPdf, HtmlPdfResult.class.getName());
        resultMap.put(ActionSupport.Markdown, MarkdownResult.class.getName());
        resultMap.put(ActionSupport.FileSave, FileSaveResult.class.getName());
        resultMap.put(ActionSupport.PdfPageImg, PdfPageImgResult.class.getName());
        resultMap.put(ActionSupport.MESSAGE, MessageResult.class.getName());
        resultMap.put(ActionSupport.CHAIN, ChainResult.class.getName());
        resultMap.put(ActionSupport.XSTREAM, XStreamResult.class.getName());
        resultMap.put(ActionSupport.QRCode, QRCodeResult.class.getName());
        resultMap.put(ActionSupport.REDIRECT, RedirectResult.class.getName());
        resultMap.put(ActionSupport.ROC, RocResult.class.getName());
        resultMap.put(ActionSupport.CHARTS, FusionChartsXmlResult.class.getName());
        resultMap.put(ActionSupport.EXCEL, ExcelResult.class.getName());
        resultMap.put(ActionSupport.PRINT, PrintResult.class.getName());
        resultMap.put(ActionSupport.FORWARD, ForwardResult.class.getName());
        resultMap.put(ActionSupport.ERROR, ErrorResult.class.getName());
    }

    private boolean executed = false;
    private String resultCode = null;
    private DefaultActionProxy actionProxy;
    private Iterator<String> interceptors;
    private ActionConfig actionConfig;
    private String namespace;


    public boolean isExecuted() {
        return executed;
    }

    public String getResultCode() {
        return resultCode;
    }

    public void setResultCode(String resultCode) {
        this.resultCode = resultCode;
    }

    public ActionProxy getActionProxy() {
        return actionProxy;
    }

    public DefaultActionInvocation(ActionConfig actionConfig, Map<String, Object> params, JSONObject jsonData, HttpServletRequest request, HttpServletResponse response) throws Exception {
        actionProxy = new DefaultActionProxy();

        namespace = (String) params.get(ActionSupport.Key_Namespace);
        String[] beanTypes = StringUtil.split(actionConfig.getType(), StringUtil.SEMICOLON);
        /////////创建bean对象 begin
        ActionSupport action;
        if (ArrayUtil.inArray(beanTypes, TXWeb.CONFIG_CLASS, true)) {
            action = (ActionSupport) ClassUtil.newInstance(actionConfig.getIocBean());
        } else {
            action = (ActionSupport) beanFactory.getBean(actionConfig.getIocBean(), namespace);
        }
        if (action == null) {
            throw new Exception("ioc get action error :" + actionConfig.getIocBean() + "   namespace=" + namespace + ",检查ioc 配置或者txweb class 部分配置");
        }
        /////////创建bean对象 end

        //设置请求参数 begin
        action.setRequest(request);
        action.setResponse(response);
        //设置请求参数 end


        //////////////载入配置参数 begin
        Placeholder placeholder = EnvFactory.getPlaceholder();
        Map<String, Object> paraMap = actionConfig.getParam();
        for (String pKey : paraMap.keySet()) {
            String sValue = placeholder.processTemplate(params, (String) paraMap.get(pKey));
            params.put(pKey, sValue);
            ////////////如果action存在此方法，就传到action里边
            if (!ArrayUtil.inArray(noParameter, pKey, true) && ClassUtil.isSetMethod(action.getClass(), pKey)) {
                BeanUtil.setSimpleProperty(action, pKey, sValue);
            }
        }

        //判断是否使用手机模式切换模版
        if (actionConfig.isMobile()) {
            params.put(ActionSupport.KEY_MobileTemplate, true);
        }
        action.getEnv().putAll(params);
        //////////////载入配置参数 end

        //上传方式比较特殊 选方参数,保证上传的设置 begin
        TXWebUtil.setMulRequest(action);
        //上传方式比较特殊 选方参数,保证上传的设置 end

        /////////放入拦截器 begin
        actionProxy.setAction(action);

        //设置执行方法 begin
        //这里可能会存在安全问题
        String methodName = actionConfig.getMethod();

        if (jsonData != null) {
            //当配置有方法的时候，只能执行配置中的方法，不能执行其他动作,避免安全漏洞
            //兼容简单方式 {"jsonrpc": "2.0", "method": "sum", "params": [1,2,4], "id": "1"},
            String requestMethodName;
            JSONObject methodJson = jsonData.getJSONObject(Environment.rocMethod);
            if (methodJson != null) requestMethodName = methodJson.getString(Environment.rocName);
            else requestMethodName = jsonData.getString(Environment.rocMethod);

            if (StringUtil.isNULL(methodName) || methodName.startsWith("@") || methodName.equals(requestMethodName))
                methodName = requestMethodName;
            else {
                log.debug(actionConfig.toString() + " 已配置了执行方法 method:" + methodName + "不允许在调用其它未指定方法");
                JSONObject errorJson = TXWebUtil.createRocErrorInfo(action.getEnv(ActionSupport.Key_ActionName), ErrorRocCode.method_not_found, actionConfig.getActionName() + " 已配置了执行方法 method:" + methodName + "不允许在调用其它未指定方法");
                TXWebUtil.print(errorJson.toString(4), TXWebUtil.jsonType, action.getResponse());
                return;
            }
            //必须先设置action
            actionProxy.setCallJson(jsonData);
        }
        actionProxy.setMethod(TXWebUtil.getInvokeMethod(action, methodName));
        //设置执行方法 end

        List<String> list = new LinkedList<String>();
        List<String> tmpList = webConfigManager.getDefaultInterceptors(namespace);
        if (tmpList != null && !tmpList.isEmpty()) list.addAll(tmpList);

        tmpList = actionConfig.getInterceptors(); //配置的拦截器
        if (tmpList != null && !tmpList.isEmpty()) list.addAll(tmpList);

        String[] notInterceptor = actionConfig.getPassInterceptor();
        if (notInterceptor != null)
            for (String name : notInterceptor) {
                list.remove(name);
            }
        interceptors = list.iterator();
        /////////放入拦截器 end
        this.actionConfig = actionConfig;
    }

    /**
     * 初始化
     *
     * @throws Exception 异常
     */
    public void initAction() throws Exception {
        ////////////////////////////// 加载参数
        ActionSupport action = actionProxy.getAction();
        if (action == null) {
            log.error("TXWeb不能创建类," + actionProxy.getActionName());
            throw new Exception("ERROR :TXWeb不能创建类");
        }
        TXWebUtil.copyRequestProperty(action);
        //////////////////////////////
    }

    /**
     * 更具配置查找ioc配置，返回配置bean
     *
     * @return 得到配置返回对象
     * @throws Exception 异常
     */
    private ResultSupport getResultSupport() throws Exception {
        //外部配置优选
        ResultConfigBean resultBean = actionConfig.getResultConfig(resultCode);
        //注释配置
        if (resultBean == null) {
            Redirect redirect = actionProxy.getAction().getClass().getAnnotation(Redirect.class);
            if (redirect != null) {
                String beanName = resultMap.get(redirect.type());
                if (beanName != null) {
                    ResultSupport resultSupport = (ResultSupport) ClassUtil.newInstance(beanName);
                    if (!StringUtil.isNULL(redirect.location())) {
                        ResultConfigBean tempResultBean = new ResultConfigBean();
                        tempResultBean.setName("*");
                        tempResultBean.setType(redirect.type());
                        tempResultBean.setValue(redirect.location());
                        resultSupport.setResultConfig(tempResultBean);
                    }
                    return resultSupport;
                }
            }
        }
        //使用默认配置
        if (resultBean == null) {
            List<ResultConfigBean> list = webConfigManager.getDefaultResults(namespace);
            if (list != null) {
                for (ResultConfigBean resultConfigBean : list) {
                    if (resultConfigBean.getName().equalsIgnoreCase(resultCode)) {
                        resultBean = resultConfigBean;
                        break;
                    }
                }
            }
            //如果一个都没有就使用 resultCode 来查找默认返回方式
            if (resultBean == null) {
                String beanName = resultMap.get(resultCode);
                if (StringUtil.hasLength(beanName)) {
                    return (ResultSupport) ClassUtil.newInstance(beanName);
                }
                return null;
            }
        }

        String resultType = resultBean.getType();
        //如果存在配置的，就载入配置参数
        ResultSupport resultSupport = null;
        //得到返回对应类型
        String beanName = resultMap.get(resultType);
        if (beanName != null) {
            resultSupport = (ResultSupport) ClassUtil.newInstance(beanName);
        }
        ////自定义扩展返回方式 begin
        if (resultSupport == null) {
            resultSupport = (ResultSupport) beanFactory.getBean(resultType, namespace);
        }
        if (resultSupport == null) {
            log.error("没有找到返回配置bean对象 " + resultType + ",not found result config " + resultType);
            throw new Exception("没有找到返回配置bean对象 " + resultType + ",not found result config " + resultType);
        }
        resultSupport.setResultConfig(resultBean);
        return resultSupport;
        //自定义扩展返回方式 end
    }

    /**
     * @return 得到action name
     */
    public String getActionName() {
        return actionProxy.getActionName();
    }

    /**
     * @return 得到命名空间
     */
    public String getNamespace() {
        return actionProxy.getNamespace();
    }

    /**
     * @return 拦截器拦截并运行
     * @throws Exception 异常
     */
    public String invoke() throws Exception {
        if (executed) return resultCode;
        if (interceptors != null && interceptors.hasNext()) {
            String name = interceptors.next();
            Interceptor interceptor = (Interceptor) beanFactory.getBean(name, namespace);
            if (interceptor == null) {
                log.error("not find interceptors name:" + name + " namespace:" + namespace);
            } else {
                interceptor.init();
                resultCode = interceptor.intercept(this);
                interceptor.destroy();
            }
        } else {
            executed = true;
            resultCode = actionProxy.execute();
        }
        return resultCode;
    }

    /**
     * 执行返回
     *
     * @throws Exception 异常
     */
    public void executeResult(Result result) throws Exception {
        if (ActionSupport.KEY_RETURN.equalsIgnoreCase(resultCode) || ActionSupport.NONE.equalsIgnoreCase(resultCode))
            return;
        try {
            if (result != null) {
                result.execute(this);
            } else {
                result = getResultSupport();
                if (result == null) result = new TemplateResult();
                result.execute(this);
            }
        } finally {
            actionProxy.destroy();
        }
    }

}